package com.example.flow.pipeline.core;

import com.example.flow.pipeline.model.result.FlowResult;
import com.example.flow.pipeline.model.PipeContext;
import lombok.extern.slf4j.Slf4j;

/**
 * @Desprication: PipeLine接口通用实现
 * @Author: YangBin
 * @Date: created in 11:10 2020/2/23
 * @Modify By:
 */
@Slf4j
public class DefaultPipeline implements PipeLine {

    private final AbstractValve head;
    private final AbstractValve tail;

    private final PipeContext pipeContext;

    public DefaultPipeline(PipeContext pipeContext) {
        this.pipeContext = pipeContext;

        tail = new DefaultValve("tail-valve", this, null);
        head = new DefaultValve("head-valve", this, () -> {
            log.info("PipeLine Start.");
            return FlowResult.ok();
        });

        head.next = tail;
        tail.prev = head;
    }

    @Override
    public PipeContext pipeContext() {
        return pipeContext;
    }

    @Override
    public PipeLine addLast(String name, AbstractFlowHandler handler) {
        final AbstractValve newValve;
        synchronized (this) {
            handler.setPipeline(this);
            newValve = newValve(name, handler);

            addLast0(newValve);
        }
        return this;
    }

    private void addLast0(AbstractValve newValve) {
        AbstractValve prev = tail.prev;
        newValve.prev = prev;
        newValve.next = tail;
        prev.next = newValve;
        tail.prev = newValve;
    }

    private AbstractValve newValve(String name, AbstractFlowHandler handler) {
        return new DefaultValve(name, this, handler);
    }

    @Override
    public FlowResult<?> start() {
        if (pipeContext == null) {
            return FlowResult.fail("pipeContext should be not null!");
        }
        if (head == null) {
            return FlowResult.fail("there's no valve in current pipeLine!");
        }
        return head.execute();
    }

}
